﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using StyleMVVM.Data.Activation;
using StyleMVVM.Data.Collections;
using StyleMVVM.DependencyInjection;
using StyleMVVM.ViewModel;
#if !DOT_NET
using Windows.UI.Core;
#endif
#if !WINDOWS_PHONE
using System.Collections.Concurrent;
#endif
#if NETFX_CORE
using Windows.Foundation;
using Windows.UI.ApplicationSettings;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;

#elif WINDOWS_PHONE
using System.Windows.Controls.Primitives;
using Rect = Windows.Foundation.Rect;

#elif DOT_NET
using System.Windows.Controls.Primitives;
using Rect = System.Windows.Rect;

#endif

namespace StyleMVVM.View.Impl
{
	public sealed class CharmService : ICharmService, IDependencyInjectionContainerAware, IActivationAware
	{
		private Rect windowSize;
		private Dictionary<string, ICharmInfo> charms;
		private ConcurrentDictionary<string, Tuple<CharmNotification, ShowCharm>> notifications;
		private Stack<string> charmStack;
		private Popup currentPopup;

		public static void RegisterExport(IDependencyInjectionContainer container)
		{
			container.Register(typeof(CharmService)).
						 As(typeof(ICharmService)).
						 CoreExport(CoreExportStartOrder.UI).
						 AndSharedPermenantly();
		}

		public IDependencyInjectionContainer Container { get; set; }

		public void Activate()
		{
			notifications = new ConcurrentDictionary<string, Tuple<CharmNotification, ShowCharm>>();

			charmStack = new Stack<string>();
			charms = new Dictionary<string, ICharmInfo>();

			foreach (object o in Container.LocateAllByType(typeof(ICharmInfo)))
			{
				if (o is ICharmInfo)
				{
					ICharmInfo charmInfo = o as ICharmInfo;

					charms[charmInfo.CharmId] = charmInfo;
				}
			}

#if NETFX_CORE
			if (Window.Current != null)
			{
				windowSize = Window.Current.Bounds;

				Window.Current.SizeChanged += CurrentOnSizeChanged;

				SettingsPane.GetForCurrentView().CommandsRequested += OnCommandsRequested;
			}
#endif
		}

		public void RegisterCharmNotify(string charmName, CharmNotification notification)
		{
			notifications.AddOrUpdate(charmName, new Tuple<CharmNotification, ShowCharm>(notification, null), (x, y) => y);
		}

		public void RegisterCharmNotify(string charmName, CharmNotification notification, ShowCharm showCharm)
		{
			notifications.AddOrUpdate(charmName, new Tuple<CharmNotification, ShowCharm>(notification, showCharm), (x, y) => y);
		}

		public void UnRegisterCharmNotify(string charmName)
		{
			Tuple<CharmNotification, ShowCharm> remove;

			notifications.TryRemove(charmName, out remove);
		}

		public void OpenCharm(string charmName)
		{
#if NETFX_CORE
			InternalOpenCharm(charmName, null, NavigationMode.New);
#else
			InternalOpenCharm(charmName, null);

#endif
		}

		public void OpenCharm(string charmName, object navigationParameter)
		{
#if !NETFX_CORE
			InternalOpenCharm(charmName, navigationParameter);
#else
			InternalOpenCharm(charmName, navigationParameter, NavigationMode.New);
#endif
		}

#if NETFX_CORE
		private void InternalOpenCharm(string charmName, object navigationParameter, NavigationMode navigationMode)
		{
#else
		private void InternalOpenCharm(string charmName, object navigationParameter)
		{
#endif
			ICharmInfo charmInfo;

			if (charms.TryGetValue(charmName, out charmInfo))
			{
				charmStack.Push(charmName);

				if (currentPopup != null)
				{
					currentPopup.IsOpen = false;
				}

				double charmSize = charmInfo.CharmSize;

				FrameworkElement pane = Container.Locate(charmInfo.ExportName) as FrameworkElement;

				if (pane != null)
				{
					currentPopup = new Popup();

					currentPopup.Closed += SettingsPopupOnClosed;

#if NETFX_CORE
					currentPopup.IsLightDismissEnabled = true;
#elif DOT_NET
					currentPopup.StaysOpen = false;
#endif
					currentPopup.Tag = charmInfo.CharmId;

					double popupHorizontal = 0.0;
					double popupVertical = 0.0;

					switch (charmInfo.Orientation)
					{
						case CharmOrientation.Top:
							currentPopup.Width = windowSize.Width;
							currentPopup.Height = charmSize;

							currentPopup.HorizontalOffset = 0;
							currentPopup.VerticalOffset = 0;

							popupVertical = windowSize.Height / 2;
							break;

						case CharmOrientation.Bottom:
							currentPopup.Width = windowSize.Width;
							currentPopup.Height = charmSize;

							currentPopup.HorizontalOffset = 0;
							currentPopup.VerticalOffset = windowSize.Height - charmSize;

							popupVertical = windowSize.Height / 2;
							break;

						case CharmOrientation.Left:
							currentPopup.Width = charmSize;
							currentPopup.Height = windowSize.Height;

							currentPopup.HorizontalOffset = 0;
							currentPopup.VerticalOffset = 0;

							popupHorizontal = windowSize.Width / 2;
							break;

						case CharmOrientation.Right:
							currentPopup.Width = charmSize;
							currentPopup.Height = windowSize.Height;

							currentPopup.HorizontalOffset = windowSize.Width - charmSize;
							currentPopup.VerticalOffset = 0;

							popupHorizontal = windowSize.Width / 2;
							break;
					}

					pane.Width = currentPopup.Width;
					pane.Height = currentPopup.Height;

					currentPopup.Child = pane;

#if NETFX_CORE
					if (charmInfo.TransitionPopup)
					{
						currentPopup.Transitions = new TransitionCollection
							                           {
								                           new PopupThemeTransition
									                           {
										                           FromHorizontalOffset = popupHorizontal,
										                           FromVerticalOffset = popupVertical
									                           }
							                           };
					}
#endif

					INavigationParameterAwareViewModel model = pane.DataContext as INavigationParameterAwareViewModel;

					if (model != null)
					{
						model.NavigationParameter = navigationParameter;
					}

					INavigationViewModel context = pane.DataContext as INavigationViewModel;

					if (context != null)
					{
						StyleNavigationEventArgs args = new StyleNavigationEventArgs();

						args.Content = pane;

#if NETFX_CORE
						args.NavigationMode = navigationMode;
#endif

#if !WINDOWS_PHONE
						args.Parameter = navigationParameter;
#endif
						context.NavigatedTo(pane, args);
					}

					currentPopup.IsOpen = true;
				}
			}
		}

		public void Back()
		{
			if (charmStack.Count > 0)
			{
				charmStack.Pop();
			}

			if (charmStack.Count == 0)
			{
#if NETFX_CORE
				SettingsPane.Show();
#endif
			}
			else
			{
#if NETFX_CORE
				InternalOpenCharm(charmStack.Pop(), null, NavigationMode.Back);
#else
				InternalOpenCharm(charmStack.Pop(),null);
#endif
			}
		}

		public void CloseCharm()
		{
			if (currentPopup != null)
			{
				currentPopup.IsOpen = false;
				currentPopup = null;
			}
		}

#if NETFX_CORE
		private void CurrentOnSizeChanged(object sender, WindowSizeChangedEventArgs windowSizeChangedEventArgs)
		{
			windowSize = Window.Current.Bounds;
		}

		private void OnCommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
		{
			foreach (ICharmInfo charmInfo in charms.Values)
			{
				if (charmInfo.ShowInAppSettings)
				{
					string charmId = charmInfo.CharmId;

					SettingsCommand cmd = new SettingsCommand(charmId,
																			charmInfo.SettingsText,
																			(x) => OpenCharm(charmId));

					args.Request.ApplicationCommands.Add(cmd);
				}
			}

			foreach (KeyValuePair<string, Tuple<CharmNotification, ShowCharm>> charmNotification in notifications)
			{
				CharmNotification notify = charmNotification.Value.Item1;
				ShowCharm showCharm = charmNotification.Value.Item2;
				string charmString = charmNotification.Key;

				if (showCharm == null || showCharm(charmString))
				{
					SettingsCommand command = new SettingsCommand(charmString, charmString, (x) => notify(charmString));

					args.Request.ApplicationCommands.Add(command);
				}
			}
		}
#endif

		private void SettingsPopupOnClosed(object sender, object o)
		{
			Popup closingPopup = (Popup)sender;
			FrameworkElement pane = closingPopup.Child as FrameworkElement;

			if (pane != null)
			{
				INavigationViewModel context = pane.DataContext as INavigationViewModel;

				if (context != null)
				{
					StyleNavigationEventArgs eventArgs = new StyleNavigationEventArgs { Content = pane };

					context.NavigatedFrom(pane, eventArgs);
				}
			}

			if (currentPopup == sender)
			{
				currentPopup = null;

				if (charmStack.Count > 0 &&
					 charmStack.Peek().Equals(((Popup)sender).Tag))
				{
					charmStack.Clear();
				}
			}
		}
	}
}